package com.todo.event.activity.application.core.eventflow;

import com.alibaba.fastjson.JSONObject;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.common.utils.DateUtils;
import com.todo.event.activity.application.core.ab.ABHelper;
import com.todo.event.activity.application.core.ab.SampleFlowConfig;
import com.todo.event.activity.application.core.frequency.FrequencyHelper;
import com.todo.event.activity.application.entity.bo.*;
import com.todo.event.activity.application.entity.context.ActivityExecuteContext;
import com.todo.event.activity.application.entity.context.ActivityRuleContext;
import com.todo.event.activity.application.entity.context.ActivityTouchContext;
import com.todo.event.activity.application.entity.context.RewardSendContext;
import com.todo.event.activity.application.handler.activityreward.RewardFactory;
import com.todo.event.activity.application.handler.activityrule.ActivityRuleFactory;
import com.todo.event.activity.application.handler.activityrule.ActivityRuleHandleChain;
import com.todo.event.activity.application.handler.activitytouch.AbstractTouchHandle;
import com.todo.event.activity.application.handler.activitytouch.TouchFactory;
import com.todo.event.activity.application.handler.dynamicwords.DynamicWordsFactory;
import com.todo.event.activity.application.service.EventActivityRecordService;
import com.todo.event.activity.application.service.EventActivityService;
import com.todo.event.activity.common.enums.EventActivityDynamicWordEnum;
import com.todo.event.activity.common.enums.EventActivityStatusEnum;
import com.todo.event.activity.common.enums.ExecuteRecordStatusEnum;
import com.todo.event.activity.common.enums.RecordTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author azhebuxing
 * @date 2025/1/9 10:24
 * @description
 */
@Slf4j
@Component
public class ActivityExecute {

    @Resource
    private EventActivityService eventActivityService;
    @Resource
    private ActivityRuleFactory activityRuleFactory;
    @Resource
    private DynamicWordsFactory dynamicWordsFactory;
    @Resource
    private ABHelper abHelper;
    @Resource
    private RewardFactory rewardFactory;
    @Resource
    private TouchFactory touchFactory;
    @Resource
    private FrequencyHelper frequencyHelper;
    @Resource
    private EventActivityRecordService eventActivityRecordService;

    ThreadPoolExecutor activityExecutor = new ThreadPoolExecutor(30, 30, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(1000));


    public Boolean execute(ActivityExecuteContext activityExecuteContext) {
        if (activityExecuteContext == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        //1、是否存在对应决策活动
        List<EventActivityBO> eventActivityBOList = eventActivityService.queryRunningEventActivityByDecision(activityExecuteContext.getDecisionId());
        if (CollectionUtils.isEmpty(eventActivityBOList)) {
            log.warn("ActivityExecute#execute 决策执行完成，不存在对应决策活动，默认执行成功 decisionId:【{}】", activityExecuteContext.getDecisionId());
            return true;
        }
        //2、依次执行决策活动
        for (EventActivityBO eventActivityBO : eventActivityBOList) {
            try {
                CompletableFuture.supplyAsync(() -> doExecute(eventActivityBO, activityExecuteContext), activityExecutor);
            } catch (Exception ex) {
                log.error("ActivityExecute#execute 决策执行失败，当前活动为:【{}】", JSONObject.toJSONString(eventActivityBO), ex);
            }
        }

        return true;
    }

    /**
     * 具体事件活动执行逻辑
     *
     * @param eventActivityBO
     * @param activityExecuteContext
     * @return
     */
    private Boolean doExecute(EventActivityBO eventActivityBO, ActivityExecuteContext activityExecuteContext) {
        if (eventActivityBO == null || activityExecuteContext == null) {
            return false;
        }
        if (!baseActivityCheck(eventActivityBO)) {
            log.warn("ActivityExecute#doExecute 活动执行失败，基础规则校验失败,当前活动无需执行 eventActivityBO:【{}】, activityExecuteContext:【{}】", JSONObject.toJSONString(eventActivityBO), JSONObject.toJSONString(activityExecuteContext));
            return false;
        }
        //基础规则判断
        ActivityRuleHandleChain activityRuleHandleChain = activityRuleFactory.generalRuleHandleChain();
        if (activityRuleHandleChain == null) {
            log.error("ActivityExecute#doExecute 活动执行失败，规则处理链为空 eventActivityBO:【{}】", JSONObject.toJSONString(eventActivityBO));
            return false;
        }
        ActivityRuleContext activityRuleContext = buildActivityRuleContext(eventActivityBO, activityExecuteContext);
        activityRuleHandleChain.execute(activityRuleContext);
        if (!activityRuleContext.isExecuteResult()) {
            log.info("ActivityExecute#doExecute 活动执行完成，规则校验不通过，无需执行 eventActivityBO:【{}】, activityExecuteContext:【{}】", JSONObject.toJSONString(eventActivityBO), JSONObject.toJSONString(activityExecuteContext));
            return true;
        }
        //对于通过规则的数据进行记录保存以及频次执行频次记录
        Long recordId = passRuleActivityHandle(eventActivityBO, activityExecuteContext);

        //如果不存在分流策略，则直接执行
        if (!abHelper.checkAllocated(String.valueOf(eventActivityBO.getId()))) {
            List<SampleFlowConfig> sampleFlowConfigList = eventActivityBO.getExecuteInfoBOList().stream().map(executeInfoBO -> {
                SampleFlowConfig sampleFlowConfig = new SampleFlowConfig();
                sampleFlowConfig.setFlowName(executeInfoBO.getFlowName());
                sampleFlowConfig.setFlowStrategyCode(executeInfoBO.getFlowCode());
                sampleFlowConfig.setFlowRatio(executeInfoBO.getFlowRatio());
                return sampleFlowConfig;
            }).collect(Collectors.toList());
            abHelper.reAllocateBucket(String.valueOf(eventActivityBO.getId()), sampleFlowConfigList, (int) DateUtils.between(DateUtils.getNowDate(), eventActivityBO.getEndTime()).toMillis());
        }

        String executeFlowCode = abHelper.inSampleGroupReturnCode(String.valueOf(eventActivityBO.getId()), activityExecuteContext.getUserId());

        List<EventActivityExecuteInfoBO> executeInfoBOList = eventActivityBO.getExecuteInfoBOList();
        for (EventActivityExecuteInfoBO executeInfoBO : executeInfoBOList) {
            if (executeInfoBO.getFlowCode().equals(executeFlowCode)) {
                boolean executeResult = executeActualFlow(eventActivityBO.getId(), executeInfoBO, activityExecuteContext);
                log.info("ActivityExecute#doExecute 活动实际执行项流程结束 activityId:【{}】，用户：【{}】，结果：【{}】 执行executeInfoBO：【{}】", eventActivityBO.getId(), activityExecuteContext.getUserId(), executeResult, JSONObject.toJSONString(executeInfoBO));
            }
        }
        //执行完成后记录状态调整
        postExecuteHandle(recordId, eventActivityBO, activityExecuteContext);
        return true;
    }

    /**
     * 事件活动执行后置操作
     *
     * @param recordId
     * @param eventActivityBO
     * @param activityExecuteContext
     */
    private void postExecuteHandle(Long recordId, EventActivityBO eventActivityBO, ActivityExecuteContext activityExecuteContext) {
        eventActivityRecordService.updateActivityRecordStatus(RecordTypeEnum.ACTIVITY, recordId, ExecuteRecordStatusEnum.EXECUTING, ExecuteRecordStatusEnum.SUCCESS);
    }

    private Long passRuleActivityHandle(EventActivityBO eventActivityBO, ActivityExecuteContext activityExecuteContext) {

        //如果存在频次规则
        if (eventActivityBO.getExecuteRule() != null && eventActivityBO.getExecuteRule().getFrequency() != null) {
            frequencyHelper.saveExecuteItemRecord(eventActivityBO.getId(), activityExecuteContext.getUserId(), activityExecuteContext.getOperateTime());
        }
        //存储活动相关记录
        ActivityRecordBO activityRecordBO = new ActivityRecordBO();
        activityRecordBO.setActivityId(eventActivityBO.getId());
        activityRecordBO.setUserId(activityExecuteContext.getUserId());
        activityRecordBO.setUk(activityExecuteContext.getUk());
        activityRecordBO.setOperateTime(activityExecuteContext.getOperateTime());
        activityRecordBO.setStatus(ExecuteRecordStatusEnum.EXECUTING);
        activityRecordBO.setOriginData(JSONObject.toJSONString(activityExecuteContext.getDecisionPayload()));
        activityRecordBO.setRetryCount(0);
        Long recordId = eventActivityRecordService.insertActivityRecord(activityRecordBO);
        if (recordId == null) {
            throw new BizException(BizErrorCodeEnum.OPERATION_FAILED, "执行记录保存失败");
        }
        return recordId;
    }

    /**
     * 实际分流逻辑执行
     *
     * @param activityId
     * @param executeInfoBO
     * @param activityExecuteContext
     * @return
     */
    private boolean executeActualFlow(Long activityId, EventActivityExecuteInfoBO executeInfoBO, ActivityExecuteContext activityExecuteContext) {

        //前置执行数据准备，保存执行记录，获取没有执行的记录记录后续操作
        List<RewardSendContext> sendContextList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(executeInfoBO.getPropertyRewardList())) {
            for (EventActivityExecuteInfoBO.PropertyReward propertyReward : executeInfoBO.getPropertyRewardList()) {
                RewardSendContext rewardSendContext = prepareRewardContext(activityId, propertyReward, executeInfoBO, activityExecuteContext);
                if (rewardSendContext != null) {
                    sendContextList.add(rewardSendContext);
                }
            }
        }

        ActivityTouchContext touchContext = null;
        if (executeInfoBO.getMessageTouch() != null) {
            touchContext = prepareTouchContext(activityId, executeInfoBO, activityExecuteContext);
        }

        //数据准备完成，开始实际执行

        if (CollectionUtils.isNotEmpty(sendContextList)) {
            boolean rewardHandle = rewardFactory.handle(sendContextList);
            //奖励层执行失败，无法进行后续操作，后续逻辑交由补偿实现，避免出现 奖励失败，用户收到触达消息后无奖励内容
            if (!rewardHandle) {
                log.error("ActivityExecute#doExecute 活动执行失败，权益执行失败 activityId:【{}】, activityExecuteContext:【{}】", activityId, JSONObject.toJSONString(activityExecuteContext));
                return false;
            }
        }

        if (touchContext != null) {
            Boolean isDynamicInfo = executeInfoBO.getMessageTouch().getIsDynamicInfo();
            if (isDynamicInfo != null && isDynamicInfo) {
                List<EventActivityDynamicWordEnum> dynamicWordsEnumList = executeInfoBO.getMessageTouch().getDynamicWords();
                Map<EventActivityDynamicWordEnum, Object> dynamicWords = dynamicWordsFactory.handle(dynamicWordsEnumList);
                if (MapUtils.isNotEmpty(dynamicWords)) {
                    for (EventActivityDynamicWordEnum dynamicWordEnum : dynamicWords.keySet()) {
                        touchContext.getPayload().put(dynamicWordEnum.getCode(), dynamicWords.get(dynamicWordEnum));
                    }
                }
            }

            AbstractTouchHandle touchHandle = touchFactory.getTouchHandle(executeInfoBO.getMessageTouch().getExecuteClassifyEnum());
            boolean handle = touchHandle.handle(touchContext);
            if (!handle) {
                log.error("ActivityExecute#doExecute 活动执行失败，触达执行失败 activityId:【{}】, activityExecuteContext:【{}】", activityId, JSONObject.toJSONString(activityExecuteContext));
                return false;
            }
        }
        return true;

    }

    private RewardSendContext prepareRewardContext(Long activityId, EventActivityExecuteInfoBO.PropertyReward propertyReward, EventActivityExecuteInfoBO executeInfoBO, ActivityExecuteContext activityExecuteContext) {
        RewardSendContext rewardSendContext = new RewardSendContext();
        ActivityRewardRecordBO activityRewardRecordBO = eventActivityRecordService.queryRewardRecord(activityId, activityExecuteContext.getUserId(), propertyReward.getRewardEnum(), activityExecuteContext.getUk());
        //存在并且已成功无需操作
        if (activityRewardRecordBO != null && activityRewardRecordBO.getStatus().equals(ExecuteRecordStatusEnum.SUCCESS)) {
            return null;
        }
        if (activityRewardRecordBO == null) {
            activityRewardRecordBO = new ActivityRewardRecordBO();
            activityRewardRecordBO.setActivityId(activityId);
            activityRewardRecordBO.setUserId(activityExecuteContext.getUserId());
            activityRewardRecordBO.setUk(activityExecuteContext.getUk());
            activityRewardRecordBO.setRewardType(propertyReward.getRewardEnum());
            activityRewardRecordBO.setStatus(ExecuteRecordStatusEnum.EXECUTING);
            activityRewardRecordBO.setOperateTime(activityExecuteContext.getOperateTime());
            activityRewardRecordBO.setOriginData(JSONObject.toJSONString(activityExecuteContext.getDecisionPayload()));
            activityRewardRecordBO.setRetryCount(0);
            Long recordId = eventActivityRecordService.insertActivityRewardRecord(activityRewardRecordBO);
            if (recordId == null) {
                log.error("ActivityExecute#executeActualFlow#insertActivityRewardRecord 奖励记录保存失败 建议人工接入观察 activityId：【{}】executeInfoBO：【{}】", activityId, JSONObject.toJSONString(executeInfoBO));
                throw new BizException(BizErrorCodeEnum.OPERATION_FAILED, "奖励记录保存失败");
            }
            rewardSendContext.setRecordId(recordId);
        } else {
            rewardSendContext.setRecordId(activityRewardRecordBO.getId());
        }
        rewardSendContext.setUserId(activityExecuteContext.getUserId());
        rewardSendContext.setGrantId(propertyReward.getGrantId());
        rewardSendContext.setRewardEnum(propertyReward.getRewardEnum());
        rewardSendContext.setUk(activityExecuteContext.getUk());
        rewardSendContext.setActivityId(activityId);
        rewardSendContext.setIsCompare(executeInfoBO.getIsCompared() != null && executeInfoBO.getIsCompared());
        return rewardSendContext;
    }

    private ActivityTouchContext prepareTouchContext(Long activityId, EventActivityExecuteInfoBO executeInfoBO, ActivityExecuteContext activityExecuteContext) {
        ActivityTouchContext context = new ActivityTouchContext();
        ActivityTouchRecordBO activityTouchRecordBO = eventActivityRecordService.queryTouchRecord(activityId, activityExecuteContext.getUserId(), executeInfoBO.getMessageTouch().getExecuteClassifyEnum(), activityExecuteContext.getUk());
        if (activityTouchRecordBO != null && activityTouchRecordBO.getStatus().equals(ExecuteRecordStatusEnum.SUCCESS)) {
            return null;
        }
        if (activityTouchRecordBO == null) {
            ActivityTouchRecordBO insertData = new ActivityTouchRecordBO();
            insertData.setActivityId(activityId);
            insertData.setUserId(activityExecuteContext.getUserId());
            insertData.setUk(activityExecuteContext.getUk());
            insertData.setTouchType(executeInfoBO.getMessageTouch().getExecuteClassifyEnum());
            insertData.setOperateTime(activityExecuteContext.getOperateTime());
            insertData.setStatus(ExecuteRecordStatusEnum.EXECUTING);
            insertData.setOriginData(JSONObject.toJSONString(activityExecuteContext.getDecisionPayload()));
            insertData.setRetryCount(0);
            Long recordId = eventActivityRecordService.insertActivityTouchRecord(insertData);
            if (recordId == null) {
                log.error("ActivityExecute#doExecute 活动执行失败，触达记录保存失败 建议人工接入观察 activityId:【{}】, activityExecuteContext:【{}】", activityId, JSONObject.toJSONString(activityExecuteContext));
                throw new BizException(BizErrorCodeEnum.OPERATION_FAILED, "触达记录保存失败");
            }
            context.setRecordId(recordId);
        } else {
            context.setRecordId(activityTouchRecordBO.getId());
        }
        context.setActivityId(activityId);
        context.setUserId(activityExecuteContext.getUserId());
        context.setUk(activityExecuteContext.getUk());
        context.getPayload().putAll(activityExecuteContext.getDecisionPayload());
        context.setExecuteClassifyEnum(executeInfoBO.getMessageTouch().getExecuteClassifyEnum());
        context.setRelationId(executeInfoBO.getMessageTouch().getRelationId());
        context.setRelationData(executeInfoBO.getMessageTouch().getRelationData());
        context.setIsInterval(executeInfoBO.getMessageTouch().getIsInterval());
        context.setIntervalTime(executeInfoBO.getMessageTouch().getIntervalTime());
        context.setIsCompare(executeInfoBO.getIsCompared() != null && executeInfoBO.getIsCompared());
        return context;
    }


    private ActivityRuleContext buildActivityRuleContext(EventActivityBO eventActivityBO, ActivityExecuteContext activityExecuteContext) {
        ActivityRuleContext activityRuleContext = new ActivityRuleContext();
        activityRuleContext.setActivityId(eventActivityBO.getId());
        activityRuleContext.setUserId(activityExecuteContext.getUserId());
        activityRuleContext.setUk(activityExecuteContext.getUk());
        activityRuleContext.setOperateTime(activityExecuteContext.getOperateTime());
        activityRuleContext.setHitBlackList(eventActivityBO.getExecuteRule().getHitBlackList());
        activityRuleContext.setBlackClassify(eventActivityBO.getExecuteRule().getBlackClassify());
        activityRuleContext.setHitCrowd(eventActivityBO.getExecuteRule().getHitCrowd());
        activityRuleContext.setCrowdClassify(eventActivityBO.getExecuteRule().getCrowdClassify());
        if (eventActivityBO.getExecuteRule().getFrequency() != null) {
            EventActivityRuleBO.Frequency frequencyBO = eventActivityBO.getExecuteRule().getFrequency();
            ActivityRuleContext.Frequency frequency = new ActivityRuleContext.Frequency();
            frequency.setCount(frequencyBO.getCount());
            frequency.setDay(frequencyBO.getDay());
            activityRuleContext.setFrequency(frequency);
        }
        return activityRuleContext;
    }

    /**
     * 基础活动逻辑校验
     *
     * @param eventActivityBO
     * @return
     */
    private boolean baseActivityCheck(EventActivityBO eventActivityBO) {
        Date current = new Date();
        if (eventActivityBO.getStartTime().after(current) || eventActivityBO.getEndTime().before(current)) {
            log.error("ActivityExecute#baseActivityCheck 活动执行失败，当前活动不在执行期间 eventActivityBO:【{}】", JSONObject.toJSONString(eventActivityBO));
            return false;
        }
        if (!eventActivityBO.getStatus().equals(EventActivityStatusEnum.RUNNING)) {
            log.error("ActivityExecute#baseActivityCheck 活动执行失败，当前活动状态为非运行中 eventActivityBO:【{}】", JSONObject.toJSONString(eventActivityBO));
            return false;
        }
        return true;
    }
}
